use std::path::PathBuf;
/*
error-chain = "*"
rand = "*"
rand_distr = "*"
clap = "*"
same-file = "*"
memmap = "*"
walkdir = "*"
glob = "*"
*/
fn file_read_and_write() -> Result<(), std::io::Error> {
    use std::fs::File;
    use std::io::{BufRead, BufReader, Write};
    let path = "test.txt";
    let mut output = File::create(path)?;
    write!(
        output,
        "{}",
        (0..100).map(|x| format!("{} \n", x)).collect::<String>()
    )?;
    let input = File::open(path)?;
    let buffered = BufReader::new(input);
    for line in buffered.lines() {
        println!("{}", line?);
    }
    Ok(())
}
fn avoid_write_in_same_file() -> Result<(), std::io::Error> {
    let path_to_read = std::path::Path::new("test.txt");
    let stdout_handle = same_file::Handle::stdout()?;
    let handle = same_file::Handle::from_path(path_to_read)?;

    if stdout_handle == handle {
        return Err(std::io::Error::new(std::io::ErrorKind::Other, "same file"));
    } else {
        let file = std::fs::File::open(&path_to_read)?;
        let file = std::io::BufReader::new(file);
        use std::io::BufRead;
        for (num, line) in file.lines().enumerate() {
            println!("{}:{}", num, line?.to_uppercase());
        }
    }
    Ok(())
}
fn memmap_use() -> Result<(), std::io::Error> {
    let file = std::fs::File::open("test.txt")?;
    let map = unsafe { memmap::Mmap::map(&file)? };
    let random_index = [1, 3, 5, 7, 9, 11, 13, 15];
    let random_bytes: Vec<u8> = random_index.iter().map(|&idx| map[idx]).collect();
    println!("{:?}", String::from_utf8(random_bytes).unwrap());
    Ok(())
}

mod file_read {
    use error_chain::error_chain;
    use walkdir::WalkDir;
    error_chain! {
        foreign_links {
            WalkDir(walkdir::Error);
            Io(std::io::Error);
            SystemTimeError(std::time::SystemTimeError);
            Glob(glob::GlobError);
            Pattern(glob::PatternError);
        }
    }
    fn select_changed_name_in_24h_files() -> Result<()> {
        let current_dir = std::env::current_dir()?;
        println!("-- {:?} --", current_dir);
        for entry in std::fs::read_dir(current_dir)? {
            let entry = entry?;
            let path = entry.path();
            let metadata = std::fs::metadata(&path)?;
            let last_modified = metadata.modified()?.elapsed()?.as_secs();
            if last_modified < 24 * 3600 as u64 && metadata.is_file() {
                println!(
                    "{:?}-{:?}",
                    last_modified,
                    path.file_name().ok_or("no filename")
                );
            }
        }
        Ok(())
    }

    fn contains_loop<P: AsRef<std::path::Path>>(
        path: P,
    ) -> std::io::Result<Option<(std::path::PathBuf, std::path::PathBuf)>> {
        use same_file::is_same_file;
        let path = path.as_ref();
        let mut path_buf = path.to_path_buf();
        while path_buf.pop() {
            if is_same_file(&path_buf, path)? {
                return Ok(Some((path_buf, path.to_path_buf())));
            } else if let Some(looped_path) = contains_loop(&path_buf)? {
                return Ok(Some(looped_path));
            }
        }
        return Ok(None);
    }

    pub fn adective_file_name() {
        use std::collections::HashMap;
        let mut filenames = HashMap::new();
        for entry in WalkDir::new(".")
            .into_iter()
            .filter_map(std::result::Result::ok)
            .filter(|e| !e.file_type().is_dir())
        {
            let f_name = String::from(entry.file_name().to_string_lossy());
            let counter = filenames.entry(f_name.clone()).or_insert(0);
            *counter += 1;
            if *counter == 2 {
                println!("{}", f_name);
            }
        }
    }

    pub fn recursively_find_all_file() -> Result<()> {
        for entry in WalkDir::new(".")
            .follow_links(true)
            .into_iter()
            .filter_map(|e| e.ok())
        {
            let f_name = entry.file_name().to_string_lossy();
            let sec = entry.metadata()?.modified()?;
            if f_name.ends_with(".json") && sec.elapsed()?.as_secs() < 86400 {
                println!("{}", f_name);
            }
        }
        Ok(())
    }
    pub fn is_not_hidden(entry: &walkdir::DirEntry) -> bool {
        entry
            .file_name()
            .to_str()
            .map(|s| entry.depth() == 0 || !s.starts_with("."))
            .unwrap_or(false)
        // walkdir::WalkDir::new(".")
        //     .into_iter()
        //     .filter_entry(|e| file_read::is_not_hidden(e))
        //     .filter_map(|v| v.ok())
        //     .for_each(|x| println!("{}", x.path().display()));
    }
    pub fn recursively_calculate_size_by_depth() {
        let total_size = WalkDir::new(".")
            .min_depth(1)
            .max_depth(3)
            .into_iter()
            .filter_map(|entry| entry.ok())
            .filter_map(|entry| entry.metadata().ok())
            .filter(|metadata| metadata.is_file())
            .fold(0, |acc, m| acc + m.len());
        println!("{}", total_size);
    }
    pub fn find_all_json_files() -> Result<()> {
        for entry in glob::glob("**/*.json")? {
            println!("{}", entry?.display());
        }
        Ok(())
    }
    pub fn search_file_ignore_case() -> Result<()> {
        let options = glob::MatchOptions {
            case_sensitive: false,
            ..Default::default()
        };
        for entry in glob::glob_with("*", op